package com.general.rest.client.rest.client;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.general.rest.client.rest.core.DefaultResponse;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * @description: HttpPostInvocation
 * @author: general
 * @version: 1.0
 * @create: 2021-10-21 20:19
 **/
public class HttpPostInvocation implements Invocation {

    private final URI uri;

    private final URL url;

    private final MultivaluedMap<String, Object> headers;

    private Entity<?> entity = null;

    HttpPostInvocation(URI uri, MultivaluedMap<String, Object> headers) {
        this.uri = uri;
        this.headers = headers;
        try {
            this.url = uri.toURL();
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException();
        }
    }

    HttpPostInvocation(URL url, MultivaluedMap<String, Object> headers) {
        this.url = url;
        this.headers = headers;
        try {
            this.uri = url.toURI();
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException();
        }
    }

    HttpPostInvocation(URL url, MultivaluedMap<String, Object> headers, Entity<?> entity) {
        this(url, headers);
        this.entity = entity;
    }

    HttpPostInvocation(URI uri, MultivaluedMap<String, Object> headers, Entity<?> entity) {
        this(uri, headers);
        this.entity = entity;
    }


    @Override
    public Invocation property(String s, Object o) {
        return null;
    }

    @Override
    public Response invoke() {
        HttpURLConnection connection = null;

        try {
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod(HttpMethod.POST);
            setRequestHeaders(connection);
            connection.setDoInput(true);
            connection.setDoOutput(true);
            // 根据mediaType 拼接post请求参数
            Object responseEntity = writeEntityIfNecessary(connection);
            // TODO Set the cookies
            int statusCode = connection.getResponseCode();
            DefaultResponse response = new DefaultResponse();
            response.setConnection(connection);
            response.setStatus(statusCode);
            response.setEntity(responseEntity);
            return response;
        } catch (IOException e) {
            // TODO Error handler
            throw new RuntimeException(e);
        }
    }

    /**
     * post请求 写入请求体方法
     * @param connection
     * @return
     */
    private Object writeEntityIfNecessary(HttpURLConnection connection) {
        if(null == connection){
            throw new IllegalArgumentException("connection must not be  null;");
        }
        String postData = null;
        if(null != entity && null != this.entity.getEntity()){
            Object entity = this.entity.getEntity();
            MediaType mediaType = this.entity.getMediaType();
            if(entity.getClass().equals(Map.class)){
                Map<String, Object> params = (Map<String, Object>)this.entity.getEntity();
                switch (mediaType.toString()){
                    // application/x-www-form-urlencoded
                    case MediaType.APPLICATION_FORM_URLENCODED:
                        postData = buildFormParameters(params);
                        break;
                    // application/json
                    case MediaType.APPLICATION_JSON:
                        postData = buildJsonParameter(params);
                        break;
                    default:
                        postData = buildJsonParameter(params);
                }
            }
            else{
                switch (mediaType.toString()){
                    // application/x-www-form-urlencoded
                    case MediaType.APPLICATION_FORM_URLENCODED:
                        throw new UnsupportedOperationException("当前不支持application/x-www-form-urlencoded方式");
                        // application/json
                    case MediaType.APPLICATION_JSON:
                        postData = buildJsonParameter(entity);
                        break;
                    default:
                        postData = buildJsonParameter(entity);
                }
            }
            connection.setRequestProperty("Content-Type", mediaType.toString());
        }
        Reader in = null;
        try {
            if(null != postData){
                connection.getOutputStream().write(postData.getBytes("UTF-8"));
            }
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (int c; (c = in.read()) >= 0;) {
                sb.append((char)c);
            }
            in.close();
            connection.disconnect();
            return sb.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 构建 application/json post 请求参数
     * @param object
     * @return
     */
    public String buildJsonParameter(Object object){
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 构建 form-data和 x-www-form-urlencoded post 请求参数
     * @param params
     * @return
     */
    public String buildFormParameters(Map<String, Object> params){
        StringBuilder postData = new StringBuilder();
        try {
            for (Map.Entry<String,Object> param : params.entrySet()) {
                if (postData.length() != 0) {
                    postData.append('&');
                }
                postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
                postData.append('=');
                postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
            }
        }catch (Exception e){
            throw new IllegalArgumentException(e);
        }
        return postData.toString();
    }

    /**
     * 设置请求头
     * @param connection
     */
    private void setRequestHeaders(HttpURLConnection connection) {
        for (Map.Entry<String, List<Object>> entry : headers.entrySet()) {
            String headerName = entry.getKey();
            for (Object headerValue : entry.getValue()) {
                connection.setRequestProperty(headerName, headerValue.toString());
            }
        }
    }

    @Override
    public <T> T invoke(Class<T> aClass) {
        Response response = invoke();
        return(T)response.getEntity();
    }

    @Override
    public <T> T invoke(GenericType<T> genericType) {
        return null;
    }

    @Override
    public Future<Response> submit() {
        return null;
    }

    @Override
    public <T> Future<T> submit(Class<T> aClass) {
        return null;
    }

    @Override
    public <T> Future<T> submit(GenericType<T> genericType) {
        return null;
    }

    @Override
    public <T> Future<T> submit(InvocationCallback<T> invocationCallback) {
        return null;
    }
}
